%%%-------------------------------------------------------------------
%%% @author Rain
%%% @doc websocket协议数据
%%% @end
%%%-------------------------------------------------------------------
-module(game_protocol_ws).
-author("Rain").
-include("network.hrl").
-include("common.hrl").

%% API
-export([deal_head/2, deal_body_len/2, deal_body/2]).

%% @doc 处理包头
deal_head(Data, NetState) ->
    <<PackFin:1, _RSV:3, OpCode:4, _Masked:1, PayloadLength:7>> = Data,
    deal_head1(PackFin, OpCode, PayloadLength, NetState).

deal_head1(_PackFin, ?WS_OP_CODE_CLOSE, _PayloadLength, NetState) ->
    ?LOG_WARN("Close ~p", [close]),
    {stop, normal, NetState};
deal_head1(_PackFin, ?WS_OP_CODE_PING, _PayloadLength, NetState) ->
    {noreply, NetState};
deal_head1(_PackFin, ?WS_OP_CODE_PONG, _PayloadLength, NetState) ->
    {noreply, NetState};
deal_head1(PackFin, OpCode, PayloadLength, NetState) ->
    NetState1 = NetState#net_state{pack_fin = PackFin, pack_info = #{op_code => OpCode}},
    case PayloadLength of
        ?WS_BIG_PACK ->
            NetState2 = NetState1#net_state{pack_step = ?NET_PACK_STEP_BODY_LEN},
            {ok, NetStateN} = game_protocol:async_recv(NetState2, ?WS_BIG_PACK_BIT_LEN);
        ?WS_MID_PACK ->
            NetState2 = NetState1#net_state{pack_step = ?NET_PACK_STEP_BODY_LEN},
            {ok, NetStateN} = game_protocol:async_recv(NetState2, ?WS_MID_PACK_BIT_LEN);
        _ when PayloadLength >= 0 andalso PayloadLength =< ?WS_SMALL_PACK ->
            NetStateN = game_protocol:async_recv_body(NetState1, PayloadLength)
    end,
    {noreply, NetStateN}.


%% @doc 处理包长度
deal_body_len(<<PayloadLength:16>>, #net_state{pack_step = ?NET_PACK_STEP_BODY_LEN} = NetState) -> % Mid Pack
    deal_body_len_normal(PayloadLength, NetState);
deal_body_len(<<PayloadLength:64>>, NetState) -> % Big Pack
    deal_body_len_normal(PayloadLength, NetState).

deal_body_len_normal(PayloadLength, NetState) ->
    NetState2 = game_protocol:async_recv_body(NetState, PayloadLength),
    {noreply, NetState2}.

%% @doc 处理包体
deal_body(<<MaskKey:32, Payload/binary>>, NetState = #net_state{pack_fin = PackFin, pack_cache = PackCache}) ->
    UnmaskPayload = unmask(Payload, MaskKey),
    Pack = << PackCache/binary, UnmaskPayload/binary >>,
    case PackFin of
        ?FALSE ->
            NetState1 = NetState#net_state{pack_cache = Pack};
        _ ->
            GateMsg = game_protocol:unpack_net_bin(Pack),
            game_protocol:deal_gate_msg(NetState, GateMsg),
            NetState1 = NetState#net_state{pack_cache = <<>>}
    end,
    % 处理完后获取下一个数据包
    NetState2 = game_protocol:async_recv_head(NetState1),
    {noreply, NetState2}.



%% @doc unmask ws的payload数据 copy from cow_ws
unmask(Data, MaskKey) ->
    unmask(Data, MaskKey, 0).
unmask(Data, undefined, _) ->
    Data;
unmask(Data, MaskKey, 0) ->
    mask(Data, MaskKey, <<>>);
%% We unmask on the fly so we need to continue from the right mask byte.
unmask(Data, MaskKey, UnmaskedLen) ->
    Left = UnmaskedLen rem 4,
    Right = 4 - Left,
    MaskKey2 = (MaskKey bsl (Left * 8)) + (MaskKey bsr (Right * 8)),
    mask(Data, MaskKey2, <<>>).

%% @doc mask ws的payload数据 copy from cow_ws
mask(<<>>, _, Unmasked) ->
    Unmasked;
mask(<< O:32, Rest/bits >>, MaskKey, Acc) ->
    T = O bxor MaskKey,
    mask(Rest, MaskKey, << Acc/binary, T:32 >>);
mask(<< O:24 >>, MaskKey, Acc) ->
    << MaskKey2:24, _:8 >> = << MaskKey:32 >>,
    T = O bxor MaskKey2,
    << Acc/binary, T:24 >>;
mask(<< O:16 >>, MaskKey, Acc) ->
    << MaskKey2:16, _:16 >> = << MaskKey:32 >>,
    T = O bxor MaskKey2,
    << Acc/binary, T:16 >>;
mask(<< O:8 >>, MaskKey, Acc) ->
    << MaskKey2:8, _:24 >> = << MaskKey:32 >>,
    T = O bxor MaskKey2,
    << Acc/binary, T:8 >>.






